package iblink.adapter;

import iblink.adapter.request.ContractDetailsRequest;
import iblink.adapter.request.HistoricalDataRequest;
import iblink.adapter.request.IRequestFactory;
import iblink.adapter.request.ScannerDataRequest;
import iblink.core.HistoricalData;
import iblink.core.ISecurity;
import iblink.core.Option;
import iblink.core.ScannerArgs;
import iblink.core.ScannerData;
import iblink.core.Stock;
import iblink.exception.IBLinkException;
import iblink.util.Constants.BarSize;
import iblink.util.Constants.DataType;
import iblink.util.Constants.Duration;
import iblink.util.Constants.Right;

import java.util.ArrayList;
import java.util.List;
import java.util.Date;

import com.ib.client.ContractDetails;
import packutil.DateUtil;
import packutil.ThreadUtil;

public class IBFacade {

	private final IBClient ibClient;// TODO: remove
	private IRequestFactory reqFactory;

	protected IBFacade(IBClient ibClient, IRequestFactory requestFactory) {
		this.reqFactory = requestFactory;
		this.ibClient = ibClient;
		// log client id and process (ie eod process)
	}

	public List<ScannerData> getScannerData(ScannerArgs scannerArgs) {
		ScannerDataRequest req = reqFactory
				.createScannerDataRequest(scannerArgs);
		req.execute();
		return req.getScannerData();
	}

	public List<HistoricalData> getHistoricalData(ISecurity sec,
			Date endDateTime, Duration duration, BarSize barSize,
			DataType whatToShow, boolean onlyRegularTradingHours) {

		HistoricalDataRequest req = reqFactory.createHistoricalDataRequest(sec,
				endDateTime, duration, barSize, whatToShow,
				onlyRegularTradingHours);
		req.execute();
		return req.getHistoricalData();

	}

	public ContractDetails getContractDetailsForStock(String symbol) {

		ContractDetailsRequest req = reqFactory
				.createContractDetailsRequest(IBHelper.getStockContract(symbol));
		req.execute();
		List<ContractDetails> contractDetailsList = req.getContractDetails();

		// TODO: add more details to exception & logs
		if (contractDetailsList.size() > 1)
			throw new RuntimeException(
					"more than 1 contract details in response");

		return contractDetailsList.isEmpty() ? null : contractDetailsList
				.get(0);
	}

	public Stock getStock(String symbol) {

		ContractDetailsRequest req = reqFactory
				.createContractDetailsRequest(IBHelper.getStockContract(symbol));
		req.execute();

		if (req.getContractDetails().isEmpty()) {
			return null;
		}

		// if more than one contract detail, try to choose one on an american
		// exchange
		Stock s = null;
		if (req.getContractDetails().size() > 1) {
			for (ContractDetails cd : req.getContractDetails()) {
				if (IBHelper.isUSExchange(cd.m_summary.m_primaryExch)) {
					s = IBHelper.parseContractDetailsToStock(cd);
					break;
				}
			}
		}

		// if there is only one contract detail, or there are multiple and none
		// are US exchanges, use that
		if (s == null) {
			s = IBHelper.parseContractDetailsToStock(req.getContractDetails()
					.get(0));
		}

		// extra validation..
		if (!symbol.equals(s.getSymbol()))
			throw new IBLinkException("Error creating Stock for symbol '"
					+ symbol + "', symbol on contract details '"
					+ s.getSymbol() + "' different from requested symbol");

		return s;
	}

	public List<Option> getOptions(ISecurity underlying) {

		ContractDetailsRequest req = reqFactory
				.createContractDetailsRequest(IBHelper
						.getOptionContractForUnderlying(underlying));
		req.execute();

		if (req.getContractDetails().isEmpty()) {
			return null;
		}

		List<Option> options = new ArrayList<Option>();

		for (ContractDetails cd : req.getContractDetails()) {
			Date expDate = DateUtil
					.parseDate(cd.m_summary.m_expiry, "yyyyMMdd");
			double strike = cd.m_summary.m_strike;
			Right right = Right.parseIbString(cd.m_summary.m_right);
			String symbol = cd.m_summary.m_localSymbol;

			options.add(new Option(underlying, symbol, right, strike, expDate));
		}

		return options;
	}

	public void tryReconnect() { // TODO: remove, already automated and moved to IBClient
		ibClient.disconnect();
		ThreadUtil.sleep(1000);
		ibClient.connect();
	}

}